#!/bin/sh
#
# This file is part of the coreboot project.
#
# Copyright (C) 2007-2010 coresystems GmbH
# Copyright (C) 2012 Google Inc
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#

TMPFILE=""

die() {
	echo "ERROR: $*" >&2
	exit 1
}

clean_up() {
	if [ -n "$TMPFILE" ]; then
		rm -f "$TMPFILE" "$TMPFILE.c" "$TMPFILE.o"
	fi
}

program_exists() {
	type "$1" >/dev/null 2>&1
}

testcc() {
	local tmp_c="$TMPFILE.c"
	local tmp_o="$TMPFILE.o"
	rm -f "$tmp_c" "$tmp_o"
	echo "void _start(void) {}" >"$tmp_c"
	"$1" -nostdlib -Werror $2 "$tmp_c" -o "$tmp_o" >/dev/null 2>&1
}

testas() {
	local gccprefixes="$1"
	local twidth="$2"
	local arch="$3"
	local use_dash_twidth="$4"
	local obj_file="$TMPFILE.o"
	local full_arch="elf$twidth-$arch"

	rm -f "$obj_file"
	[ -n "$use_dash_twidth" ] && use_dash_twidth="--$twidth"
	${gccprefixes}as $use_dash_twidth -o "$obj_file" $TMPFILE 2>/dev/null ||
		return 1

	# Check output content type.
	local obj_type="$(${gccprefixes}objdump -p $obj_file)"
	local obj_arch="$(expr "$obj_type" : '.*format \(.[a-z0-9-]*\)')"
	[ "$obj_arch" = "$full_arch" ] || return 1

	# Architecture matched.
	GCCPREFIX="$gccprefixes"

	if [ -z "$use_dash_twidth" ]; then
		ASFLAGS=""
		CFLAGS=""
		LDFLAGS=""
	else
		ASFLAGS="--$twidth"
		CFLAGS="-m$twidth"
		LDFLAGS="-b $full_arch"

	fi

	# Special parameters only available in dash_twidth mode.
	[ -n "$use_dash_twidth" ] && case "$full_arch" in
		"elf32-i386" )
			LDFLAGS="$LDFLAGS -melf_i386"
			CFLAGS="$CFLAGS -Wl,-b,elf32-i386 -Wl,-melf_i386"
			;;
	esac

	return 0
}

detect_special_flags() {
	local architecture="$1"
	# GCC 4.6 is much more picky about unused variables.
	# Turn off it's warnings for now:
	testcc "$CC"   "$CFLAGS -Wno-unused-but-set-variable " &&
		CFLAGS="$CFLAGS -Wno-unused-but-set-variable "

	testcc "$CC"   "$CFLAGS -Wa,--divide" &&
		CFLAGS="$CFLAGS -Wa,--divide"
	testcc "$CC"   "$CFLAGS -fno-stack-protector"&&
		CFLAGS="$CFLAGS -fno-stack-protector"
	testcc "$CC"   "$CFLAGS -Wl,--build-id=none" &&
		CFLAGS="$CFLAGS -Wl,--build-id=none"

	case "$architecture" in
	x86 )
		# Use bfd linker instead of gold if available:
		testcc "$CC"   "$CFLAGS -fuse-ld=bfd" &&
			CFLAGS="$CFLAGS -fuse-ld=bfd" && LINKER_SUFFIX_i386='.bfd'
		# Always build for i686 -- no sse/mmx instructions since SMM
		# modules are compiled using these flags. Note that this
		# doesn't prevent a project using xcompile to explicitly
		# specify -mmsse, etc flags.
		CFLAGS="$CFLAGS  -march=i686"
		;;
	arm )
		ARMFLAGS=""
		testcc "$CC" "$CFLAGS $ARMFLAGS" && CFLAGS="$CFLAGS $ARMFLAGS"
		;;
	esac
}

report_arch_toolchain() {
	cat <<EOF
# elf${TWIDTH}-${TBFDARCH} toolchain (${GCCPREFIX}gcc)
CC_${TARCH}:=${GCCPREFIX}gcc ${CFLAGS}
AS_${TARCH}:=${GCCPREFIX}as ${ASFLAGS}
LD_${TARCH}:=${GCCPREFIX}ld$(eval echo \${LINKER_SUFFIX_${TARCH}}) ${LDFLAGS}
NM_${TARCH}:=${GCCPREFIX}nm
OBJCOPY_${TARCH}:=${GCCPREFIX}objcopy
OBJDUMP_${TARCH}:=${GCCPREFIX}objdump
READELF_${TARCH}:=${GCCPREFIX}readelf
STRIP_${TARCH}:=${GCCPREFIX}strip
AR_${TARCH}:=${GCCPREFIX}ar

EOF
}

# Create temporary file(s).
TMPFILE="$(mktemp /tmp/temp.XXXX 2>/dev/null || echo /tmp/temp.78gOIUGz)"
touch "$TMPFILE"
trap clean_up EXIT

# Architecture definition
SUPPORTED_ARCHITECTURE="x86 arm"

# ARM Architecture
TARCH_arm="arm"
TBFDARCH_arm="littlearm"
TCLIST_arm="armv7a"
TWIDTH_arm="32"

# X86 Architecture
TARCH_x86="i386"
TBFDARCH_x86="i386"
TCLIST_x86="i386 x86_64"
TWIDTH_x86="32"
XGCCPATH=${1:-"`pwd`/util/crossgcc/xgcc/bin/"}

# This loops over all supported architectures.
for architecture in $SUPPORTED_ARCHITECTURE; do
	GCCPREFIX="invalid"
	TARCH="$(eval echo \$TARCH_$architecture)"
	TBFDARCH="$(eval echo \$TBFDARCH_$architecture)"
	TCLIST="$(eval echo \$TCLIST_$architecture)"
	TWIDTH="$(eval echo \$TWIDTH_$architecture)"
	[ -z "$TARCH" -o -z "$TCLIST" -o -z "$TWIDTH" ] &&
		die "Missing architecture definition for $architecture."

	# To override toolchain, define CROSS_COMPILE_$arch or CROSS_COMPILE as
	# environment variable.
	# Ex: CROSS_COMPILE_arm="armv7a-cros-linux-gnueabi-"
	#     CROSS_COMPILE_x86="i686-pc-linux-gnu-"
	search="$(eval echo \$CROSS_COMPILE_$architecture 2>/dev/null)"
	search="$search $CROSS_COMPILE"
	for toolchain in $TCLIST; do
		search="$search $XGCCPATH$toolchain-elf-"
		search="$search $toolchain-elf-"
		search="$search $XGCCPATH$toolchain-eabi-"
		search="$search $toolchain-eabi-"
	done
	echo "# $architecture TARCH_SEARCH=$search"

	# Search toolchain by checking assembler capability.
	for gccprefixes in $search ""; do
		program_exists "${gccprefixes}as" || continue
		testas "$gccprefixes" "$TWIDTH" "$TBFDARCH" "" && break
		testas "$gccprefixes" "$TWIDTH" "$TBFDARCH" "TRUE" && break
	done

	if [ "$GCCPREFIX" = "invalid" ]; then
		echo "Warning: no suitable GCC for $architecture." >&2
		continue
	fi
	CC="${GCCPREFIX}"gcc

	detect_special_flags "$architecture"
	report_arch_toolchain
done

if [ "$(${XGCCPATH}/iasl 2>/dev/null | grep -c ACPI)" -gt 0 ]; then
	IASL=${XGCCPATH}iasl
else
	IASL=iasl
fi

if program_exists gcc; then
	HOSTCC=gcc
else
	HOSTCC=cc
fi

cat <<EOF
IASL:=${IASL}

# native toolchain
HOSTCC:=${HOSTCC}
EOF
